using Microsoft.Extensions.Logging;
using SeataNet.Core.Common;
using SeataNet.Core.Configuration;
using SeataNet.Core.Context;
using SeataNet.Core.Exceptions;
using SeataNet.Core.Protocol;
using SeataNet.Core.RPC;
using SeataNet.Core.Transaction;
using System.Collections.Concurrent;
using System.Data;
using System.Data.Common;
using System.Text.Json;

namespace SeataNet.Core.AT
{
    /// <summary>
    /// AT模式事务管理器接口
    ///
    /// AT模式（Automatic Transaction，自动事务模式）是Seata最核心和最常用的事务模式。
    ///
    /// AT模式特点：
    /// 1. **自动化**：自动生成undo_log，无需手动编写补偿逻辑
    /// 2. **无侵入**：对业务代码几乎无侵入，只需添加注解
    /// 3. **性能好**：一阶段直接提交，二阶段异步处理
    /// 4. **隔离性**：通过全局锁保证读已提交隔离级别
    ///
    /// 工作原理：
    /// 第一阶段（Prepare）：
    /// - 解析SQL，获取要修改的数据
    /// - 查询修改前镜像（before image）
    /// - 执行业务SQL
    /// - 查询修改后镜像（after image）
    /// - 生成undo_log插入数据库
    /// - 提交本地事务，释放本地锁
    ///
    /// 第二阶段（Commit/Rollback）：
    /// - 提交：异步删除undo_log，释放全局锁
    /// - 回滚：根据undo_log生成反向SQL，恢复数据，释放全局锁
    ///
    /// 适用场景：
    /// - 大部分的分布式事务场景
    /// - 对性能要求较高的场景
    /// - 基于关系型数据库的微服务架构
    ///
    /// 限制条件：
    /// - 数据库必须支持本地ACID事务
    /// - 需要有主键或唯一索引
    /// - SQL必须可解析（不支持存储过程）
    /// </summary>
    public interface IAtTransactionManager
    {
        /// <summary>
        /// 异步开始AT事务
        /// </summary>
        /// <param name="xid">全局事务ID</param>
        /// <param name="timeout">超时时间（毫秒）</param>
        /// <param name="cancellationToken">取消令牌</param>
        Task BeginAsync(string xid, long timeout, CancellationToken cancellationToken = default);

        /// <summary>
        /// 异步提交AT事务
        /// </summary>
        /// <param name="xid">全局事务ID</param>
        /// <param name="cancellationToken">取消令牌</param>
        Task CommitAsync(string xid, CancellationToken cancellationToken = default);

        /// <summary>
        /// 异步回滚AT事务
        /// </summary>
        /// <param name="xid">全局事务ID</param>
        /// <param name="cancellationToken">取消令牌</param>
        Task RollbackAsync(string xid, CancellationToken cancellationToken = default);

        /// <summary>
        /// 异步注册AT分支事务
        /// </summary>
        /// <param name="xid">全局事务ID</param>
        /// <param name="resourceId">资源ID（数据库连接串）</param>
        /// <param name="lockKey">锁键（表名:主键值）</param>
        /// <param name="cancellationToken">取消令牌</param>
        Task RegisterBranchAsync(string xid, string resourceId, string lockKey, CancellationToken cancellationToken = default);

        /// <summary>
        /// 异步报告分支事务状态
        /// </summary>
        /// <param name="xid">全局事务ID</param>
        /// <param name="branchId">分支事务ID</param>
        /// <param name="status">分支状态</param>
        /// <param name="cancellationToken">取消令牌</param>
        Task ReportStatusAsync(string xid, long branchId, BranchStatus status, CancellationToken cancellationToken = default);

        /// <summary>
        /// 异步获取分支事务状态
        /// </summary>
        /// <param name="xid">全局事务ID</param>
        /// <param name="branchId">分支事务ID</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>分支事务状态</returns>
        Task<BranchStatus> GetBranchStatusAsync(string xid, long branchId, CancellationToken cancellationToken = default);

        // 同步方法别名
        /// <summary>
        /// 同步开始事务
        /// </summary>
        /// <param name="timeout">超时时间（毫秒）</param>
        /// <param name="name">事务名称（可选）</param>
        /// <returns>全局事务ID</returns>
        string Begin(int timeout, string? name = null);

        /// <summary>
        /// 同步提交事务
        /// </summary>
        /// <param name="xid">全局事务ID</param>
        void Commit(string xid);

        /// <summary>
        /// 同步回滚事务
        /// </summary>
        /// <param name="xid">全局事务ID</param>
        void Rollback(string xid);

        /// <summary>
        /// 同步注册分支
        /// </summary>
        /// <param name="xid">全局事务ID</param>
        /// <param name="resourceId">资源ID</param>
        /// <param name="lockKey">锁键</param>
        void RegisterBranch(string xid, string resourceId, string lockKey);

        /// <summary>
        /// 同步报告分支状态
        /// </summary>
        /// <param name="xid">全局事务ID</param>
        /// <param name="branchId">分支事务ID</param>
        /// <param name="status">分支状态</param>
        void ReportBranchStatus(string xid, long branchId, BranchStatus status);
    }

    /// <summary>
    /// 默认AT模式事务管理器实现
    ///
    /// 职责：
    /// 1. 管理AT模式的全局事务和分支事务
    /// 2. 与TC通信，发送AT相关的请求
    /// 3. 维护事务上下文信息
    /// 4. 协调undo_log的生成和处理
    ///
    /// 线程安全：
    /// - 使用ConcurrentDictionary存储事务上下文，支持并发访问
    /// - RPC客户端是线程安全的
    ///
    /// 注意事项：
    /// - Begin方法中的XID应该由全局事务管理器生成，这里简化实现
    /// - 实际生产环境中应该集成完整的事务管理器
    /// </summary>
    public class DefaultAtTransactionManager : IAtTransactionManager
    {
        private readonly IRpcClient _rpcClient;
        private readonly ILogger<DefaultAtTransactionManager> _logger;
        // 事务上下文字典，key为XID，value为事务上下文
        private readonly ConcurrentDictionary<string, AtTransactionContext> _transactionContexts;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="rpcClient">RPC客户端，用于与TC通信</param>
        /// <param name="logger">日志记录器</param>
        public DefaultAtTransactionManager(
            IRpcClient rpcClient,
            ILogger<DefaultAtTransactionManager> logger)
        {
            _rpcClient = rpcClient;
            _logger = logger;
            _transactionContexts = new ConcurrentDictionary<string, AtTransactionContext>();
        }

        /// <summary>
        /// 异步开始AT事务
        ///
        /// 执行流程：
        /// 1. 验证XID参数的有效性
        /// 2. 构造GlobalBeginRequest请求，包含事务名称和超时时间
        /// 3. 通过RPC客户端发送请求到TC（事务协调器）
        /// 4. 接收TC返回的响应，包含XID和TransactionId
        /// 5. 创建并保存AtTransactionContext到内存字典中
        /// 6. 记录事务开始日志
        ///
        /// 调用时机：
        /// - 在业务逻辑开始前调用
        /// - 通常由@GlobalTransactional注解或拦截器自动触发
        /// - 在RootContext中绑定XID之前
        ///
        /// 注意事项：
        /// - XID应该由全局事务管理器生成，此处接收已生成的XID
        /// - 超时时间决定了整个AT事务的有效期限
        /// - 事务上下文存储在ConcurrentDictionary中，支持并发访问
        /// - 如果TC不可达或拒绝请求，会抛出TransactionException
        /// </summary>
        /// <param name="xid">全局事务ID，必须是有效的非空字符串</param>
        /// <param name="timeout">超时时间（毫秒），超时后TC会自动回滚</param>
        /// <param name="cancellationToken">取消令牌，用于取消异步操作</param>
        /// <exception cref="ArgumentNullException">XID为空时抛出</exception>
        /// <exception cref="TransactionException">开始事务失败时抛出，通常是TC不可达或拒绝请求</exception>
        public async Task BeginAsync(string xid, long timeout, CancellationToken cancellationToken = default)
        {
            if (string.IsNullOrWhiteSpace(xid))
            {
                throw new ArgumentNullException(nameof(xid));
            }

            _logger.LogDebug($"开始AT事务: XID={xid}, Timeout={timeout}ms");

            try
            {
                var request = new GlobalBeginRequest
                {
                    TransactionName = "AT Transaction",
                    Timeout = (int)timeout
                };

                var response = await _rpcClient.SendAsync<GlobalBeginRequest, GlobalBeginResponse>(request, cancellationToken);
                
                if (response == null)
                {
                    throw new TransactionException("开始AT事务失败：未收到响应");
                }

                if (response.ResultCode != ResultCode.Success)
                {
                    throw new TransactionException($"开始AT事务失败：{response.Message}");
                }

                // 保存事务上下文
                var context = new AtTransactionContext
                {
                    Xid = response.Xid,
                    TransactionId = response.TransactionId,
                    Status = TransactionStatus.Begin,
                    Timeout = timeout,
                    BeginTime = DateTime.UtcNow
                };

                _transactionContexts[xid] = context;
                _logger.LogDebug($"AT事务已开始: XID={response.Xid}, TransactionId={response.TransactionId}");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"开始AT事务失败: XID={xid}");
                throw new TransactionException($"开始AT事务失败: XID={xid}", ex);
            }
        }

        /// <summary>
        /// 异步提交AT事务
        ///
        /// 这是AT事务的第二阶段提交操作，通知TC提交所有分支事务。
        ///
        /// 执行流程：
        /// 1. 验证XID参数的有效性
        /// 2. 从事务上下文字典中查找对应的事务上下文
        /// 3. 构造GlobalCommitRequest请求，包含XID
        /// 4. 通过RPC客户端发送提交请求到TC
        /// 5. TC收到请求后异步通知所有分支执行第二阶段提交：
        ///    - 删除undo_log表中对应的记录
        ///    - 释放TC中的全局锁
        ///    - 清理分支相关的资源
        /// 6. 更新事务上下文的状态为Committed，并记录结束时间
        /// 7. 记录提交成功日志
        ///
        /// AT模式的两阶段提交：
        /// - 第一阶段（BeginAsync）：执行SQL + 生成undo_log + 本地提交
        /// - 第二阶段（此方法）：异步删除undo_log + 释放锁
        ///
        /// 调用时机：
        /// - 业务逻辑正常执行完成后
        /// - 通常由@GlobalTransactional注解或拦截器自动触发
        /// - 在所有分支事务都执行成功后
        ///
        /// 注意事项：
        /// - 该方法会阻塞等待TC响应
        /// - 提交是异步的，TC会后台协调各分支提交
        /// - 提交失败通常是由于网络问题或TC不可达
        /// - 即使提交失败，第一阶段的数据修改已经持久化（本地事务已提交）
        /// - 事务上下文会保留在内存中，直到应用重启
        /// </summary>
        /// <param name="xid">全局事务ID，必须是有效的XID</param>
        /// <param name="cancellationToken">取消令牌，用于取消异步操作</param>
        /// <exception cref="ArgumentNullException">XID为空时抛出</exception>
        /// <exception cref="TransactionException">提交失败时抛出，可能是TC不可达或未找到事务上下文</exception>
        public async Task CommitAsync(string xid, CancellationToken cancellationToken = default)
        {
            if (string.IsNullOrWhiteSpace(xid))
            {
                throw new ArgumentNullException(nameof(xid));
            }

            _logger.LogDebug($"提交AT事务: XID={xid}");

            try
            {
                if (!_transactionContexts.TryGetValue(xid, out var context))
                {
                    throw new TransactionException($"未找到事务上下文: XID={xid}");
                }

                var request = new GlobalCommitRequest
                {
                    Xid = xid
                };

                var response = await _rpcClient.SendAsync<GlobalCommitRequest, GlobalCommitResponse>(request, cancellationToken);
                
                if (response == null)
                {
                    throw new TransactionException("提交AT事务失败：未收到响应");
                }

                if (response.ResultCode != ResultCode.Success)
                {
                    throw new TransactionException($"提交AT事务失败：{response.Message}");
                }

                // 更新事务上下文
                context.Status = TransactionStatus.Committed;
                context.EndTime = DateTime.UtcNow;
                
                _logger.LogDebug($"AT事务已提交: XID={xid}");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"提交AT事务失败: XID={xid}");
                throw new TransactionException($"提交AT事务失败: XID={xid}", ex);
            }
        }

        /// <summary>
        /// 异步回滚AT事务
        ///
        /// 这是AT事务的第二阶段回滚操作，通知TC回滚所有分支事务。
        ///
        /// 执行流程：
        /// 1. 验证XID参数的有效性
        /// 2. 从事务上下文字典中查找对应的事务上下文
        /// 3. 构造GlobalRollbackRequest请求，包含XID
        /// 4. 通过RPC客户端发送回滚请求到TC
        /// 5. TC收到请求后异步通知所有分支执行第二阶段回滚：
        ///    - 从undo_log表查询对应的记录
        ///    - 解析undo_log，获取before image（修改前镜像）
        ///    - 生成反向SQL（将after image恢复到before image）
        ///    - 执行反向SQL，将数据恢复到事务前状态
        ///    - 删除undo_log记录
        ///    - 释放TC中的全局锁
        /// 6. 更新事务上下文的状态为RolledBack，并记录结束时间
        /// 7. 记录回滚成功日志
        ///
        /// AT模式的两阶段回滚：
        /// - 第一阶段（BeginAsync）：执行SQL + 捕获before/after image + 生成undo_log + 本地提交
        /// - 第二阶段（此方法）：查询undo_log + 生成反向SQL + 执行 + 删除undo_log + 释放锁
        ///
        /// 调用时机：
        /// - 业务逻辑执行异常时
        /// - 手动调用GlobalTransaction.rollback()时
        /// - 事务超时时由TC自动触发
        /// - 任何分支事务执行失败时
        ///
        /// 注意事项：
        /// - 该方法会阻塞等待TC响应
        /// - 回滚是异步的，TC会后台协调各分支回滚
        /// - 回滚依赖undo_log，如果undo_log损坏或丢失，回滚会失败
        /// - 回滚失败会记录日志，需要人工介入处理
        /// - 数据可能被其他事务修改，需要版本检查（脏写检测）
        /// - 回滚操作应该是幂等的（重复回滚不会有副作用）
        /// </summary>
        /// <param name="xid">全局事务ID，必须是有效的XID</param>
        /// <param name="cancellationToken">取消令牌，用于取消异步操作</param>
        /// <exception cref="ArgumentNullException">XID为空时抛出</exception>
        /// <exception cref="TransactionException">回滚失败时抛出，可能是TC不可达或未找到事务上下文</exception>
        public async Task RollbackAsync(string xid, CancellationToken cancellationToken = default)
        {
            if (string.IsNullOrWhiteSpace(xid))
            {
                throw new ArgumentNullException(nameof(xid));
            }

            _logger.LogDebug($"回滚AT事务: XID={xid}");

            try
            {
                if (!_transactionContexts.TryGetValue(xid, out var context))
                {
                    throw new TransactionException($"未找到事务上下文: XID={xid}");
                }

                var request = new GlobalRollbackRequest
                {
                    Xid = xid
                };

                var response = await _rpcClient.SendAsync<GlobalRollbackRequest, GlobalRollbackResponse>(request, cancellationToken);
                
                if (response == null)
                {
                    throw new TransactionException("回滚AT事务失败：未收到响应");
                }

                if (response.ResultCode != ResultCode.Success)
                {
                    throw new TransactionException($"回滚AT事务失败：{response.Message}");
                }

                // 更新事务上下文
                context.Status = TransactionStatus.RolledBack;
                context.EndTime = DateTime.UtcNow;
                
                _logger.LogDebug($"AT事务已回滚: XID={xid}");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"回滚AT事务失败: XID={xid}");
                throw new TransactionException($"回滚AT事务失败: XID={xid}", ex);
            }
        }

        /// <summary>
        /// 异步注册AT分支事务
        ///
        /// 在全局事务下注册一个AT分支事务，分支事务代表对某个资源（通常是数据库）的操作。
        ///
        /// 执行流程：
        /// 1. 验证XID、resourceId和lockKey参数的有效性
        /// 2. 构造BranchRegisterRequest请求，包含：
        ///    - XID：全局事务ID
        ///    - BranchType：设置为AT模式
        ///    - ResourceId：资源标识（数据库连接串或数据源名称）
        ///    - LockKey：锁键（表名:主键值）
        /// 3. 通过RPC客户端发送分支注册请求到TC
        /// 4. TC执行以下操作：
        ///    - 分配唯一的分支ID
        ///    - 检查lockKey是否被其他全局事务锁定
        ///    - 如果无冲突，在TC中记录该分支并持有全局锁
        ///    - 如果有冲突，返回锁冲突错误
        /// 5. 接收TC返回的分支ID
        /// 6. 记录分支注册成功日志
        ///
        /// 注册时机：
        /// - 在执行SQL之前注册分支
        /// - 确保XID已存在（全局事务已开始）
        /// - 在数据库连接上下文中调用
        /// - 通常由SQL拦截器或连接代理自动调用
        ///
        /// lockKey说明：
        /// - AT模式：格式为"表名:主键值"，如"user_table:1001"
        /// - 用于行锁的并发控制
        /// - 多个行锁用分号分隔，如"user_table:1001;user_table:1002"
        /// - TC根据lockKey检测并发冲突
        /// - 同一个XID可以重复锁定相同的行
        ///
        /// 锁机制：
        /// - AT模式：TC根据lockKey锁定相关数据行
        /// - 如果其他全局事务已锁定，当前注册会失败
        /// - 锁会在第二阶段（提交或回滚）释放
        /// - 本地锁在第一阶段提交后立即释放，只保留全局锁
        ///
        /// 异常处理：
        /// - 锁冲突：TransactionException，需要重试或回滚
        /// - TC不可达：TransactionException，网络问题
        /// - 无效响应：TransactionException，branchId解析失败
        /// </summary>
        /// <param name="xid">全局事务ID</param>
        /// <param name="resourceId">资源ID，通常是数据库连接串或数据源名称</param>
        /// <param name="lockKey">锁键，用于行锁的并发控制，格式为"表名:主键值"</param>
        /// <param name="cancellationToken">取消令牌，用于取消异步操作</param>
        /// <exception cref="ArgumentNullException">XID、resourceId或lockKey为空时抛出</exception>
        /// <exception cref="TransactionException">注册失败时抛出，可能是锁冲突或TC不可达</exception>
        public async Task RegisterBranchAsync(string xid, string resourceId, string lockKey, CancellationToken cancellationToken = default)
        {
            if (string.IsNullOrWhiteSpace(xid))
            {
                throw new ArgumentNullException(nameof(xid));
            }

            if (string.IsNullOrWhiteSpace(resourceId))
            {
                throw new ArgumentNullException(nameof(resourceId));
            }

            if (string.IsNullOrWhiteSpace(lockKey))
            {
                throw new ArgumentNullException(nameof(lockKey));
            }

            _logger.LogDebug($"注册AT分支事务: XID={xid}, ResourceId={resourceId}, LockKey={lockKey}");

            try
            {
                var request = new BranchRegisterRequest
                {
                    Xid = xid,
                    BranchType = BranchType.AT,
                    ResourceId = resourceId,
                    LockKey = lockKey
                };

                var response = await _rpcClient.SendAsync<BranchRegisterRequest, BranchRegisterResponse>(request, cancellationToken);
                
                if (response == null)
                {
                    throw new TransactionException("注册AT分支事务失败：未收到响应");
                }

                if (response.ResultCode != ResultCode.Success)
                {
                    throw new TransactionException($"注册AT分支事务失败：{response.Message}");
                }

                _logger.LogDebug($"AT分支事务已注册: XID={xid}, BranchId={response.BranchId}");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"注册AT分支事务失败: XID={xid}, ResourceId={resourceId}");
                throw new TransactionException($"注册AT分支事务失败: XID={xid}, ResourceId={resourceId}", ex);
            }
        }

        /// <summary>
        /// 异步报告AT分支事务状态
        ///
        /// 分支在执行本地事务后，向TC上报执行结果（成功或失败）。
        ///
        /// 执行流程：
        /// 1. 验证XID参数的有效性
        /// 2. 构造BranchReportRequest请求，包含：
        ///    - XID：全局事务ID
        ///    - BranchId：分支事务ID
        ///    - BranchType：设置为AT模式
        ///    - Status：分支状态枚举值
        /// 3. 通过RPC客户端发送状态报告请求到TC
        /// 4. TC记录该分支的状态
        /// 5. TC根据所有分支的状态决定全局事务的下一步操作
        /// 6. 记录状态上报成功日志
        ///
        /// 状态类型说明：
        /// - PhaseOneDone: 第一阶段完成（本地事务提交成功，undo_log已生成）
        /// - PhaseOneFailed: 第一阶段失败（本地事务执行失败）
        /// - PhaseTwoCommitted: 第二阶段提交完成（undo_log已删除）
        /// - PhaseTwoRollbacked: 第二阶段回滚完成（已根据undo_log恢复数据）
        ///
        /// 上报时机：
        /// - 第一阶段：本地事务执行完成后（成功或失败都要上报）
        /// - 第二阶段：收到TC的提交或回滚指令并执行完成后
        /// - 在SQL执行完成后，undo_log生成之后
        /// - 在分支提交或回滚完成后
        ///
        /// 注意事项：
        /// - 状态上报失败会抛出异常
        /// - TC依赖状态上报来决定全局事务的最终结果
        /// - 建议实现重试机制以确保状态最终到达TC
        /// - 上报PhaseOneFailed会触发全局回滚
        /// - 上报PhaseOneDone后等待TC的第二阶段指令
        /// </summary>
        /// <param name="xid">全局事务ID</param>
        /// <param name="branchId">分支事务ID，由RegisterBranchAsync返回</param>
        /// <param name="status">分支事务状态</param>
        /// <param name="cancellationToken">取消令牌，用于取消异步操作</param>
        /// <exception cref="ArgumentNullException">XID为空时抛出</exception>
        /// <exception cref="TransactionException">上报失败时抛出，通常是TC不可达</exception>
        public async Task ReportStatusAsync(string xid, long branchId, BranchStatus status, CancellationToken cancellationToken = default)
        {
            if (string.IsNullOrWhiteSpace(xid))
            {
                throw new ArgumentNullException(nameof(xid));
            }

            _logger.LogDebug($"报告AT分支事务状态: XID={xid}, BranchId={branchId}, Status={status}");

            try
            {
                var request = new BranchReportRequest
                {
                    Xid = xid,
                    BranchId = branchId,
                    BranchType = BranchType.AT,
                    Status = status
                };

                var response = await _rpcClient.SendAsync<BranchReportRequest, BranchReportResponse>(request, cancellationToken);
                
                if (response == null)
                {
                    throw new TransactionException("报告AT分支事务状态失败：未收到响应");
                }

                if (response.ResultCode != ResultCode.Success)
                {
                    throw new TransactionException($"报告AT分支事务状态失败：{response.Message}");
                }

                _logger.LogDebug($"AT分支事务状态已报告: XID={xid}, BranchId={branchId}");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"报告AT分支事务状态失败: XID={xid}, BranchId={branchId}");
                throw new TransactionException($"报告AT分支事务状态失败: XID={xid}, BranchId={branchId}", ex);
            }
        }

        /// <summary>
        /// 异步获取AT分支事务状态
        ///
        /// 查询指定分支的当前执行状态。
        ///
        /// 执行流程：
        /// 1. 验证XID参数的有效性
        /// 2. 构造BranchStatusRequest请求，包含：
        ///    - XID：全局事务ID
        ///    - BranchId：分支事务ID
        ///    - BranchType：设置为AT模式
        /// 3. 通过RPC客户端发送状态查询请求到TC
        /// 4. TC从分支记录中查询该分支的当前状态
        /// 5. 返回分支状态枚举值
        /// 6. 记录状态查询成功日志
        ///
        /// 状态类型说明：
        /// - Unknown: 未知状态（分支未注册或已被清理）
        /// - Registered: 已注册但未执行
        /// - PhaseOneDone: 第一阶段完成
        /// - PhaseOneFailed: 第一阶段失败
        /// - PhaseTwoCommitted: 第二阶段提交完成
        /// - PhaseTwoRollbacked: 第二阶段回滚完成
        /// - PhaseTwoCommitFailed: 第二阶段提交失败（需要重试）
        /// - PhaseTwoRollbackFailed: 第二阶段回滚失败（需要人工介入）
        ///
        /// 使用场景：
        /// - 事务恢复时需要确认分支状态
        /// - 事务监控需要查看分支执行情况
        /// - 分支异常排查
        /// - 判断是否需要重试或人工介入
        ///
        /// 注意事项：
        /// - 查询失败会抛出TransactionException
        /// - 状态查询不会改变分支的状态
        /// - 返回Unknown状态可能表示分支已完成并被TC清理
        /// - 建议在状态查询失败时实现重试机制
        /// </summary>
        /// <param name="xid">全局事务ID</param>
        /// <param name="branchId">分支事务ID</param>
        /// <param name="cancellationToken">取消令牌，用于取消异步操作</param>
        /// <returns>分支事务的当前状态</returns>
        /// <exception cref="ArgumentNullException">XID为空时抛出</exception>
        /// <exception cref="TransactionException">查询失败时抛出，通常是TC不可达</exception>
        public async Task<BranchStatus> GetBranchStatusAsync(string xid, long branchId, CancellationToken cancellationToken = default)
        {
            if (string.IsNullOrWhiteSpace(xid))
            {
                throw new ArgumentNullException(nameof(xid));
            }

            _logger.LogDebug($"获取AT分支事务状态: XID={xid}, BranchId={branchId}");

            try
            {
                var request = new BranchStatusRequest
                {
                    Xid = xid,
                    BranchId = branchId,
                    BranchType = BranchType.AT
                };

                var response = await _rpcClient.SendAsync<BranchStatusRequest, BranchStatusResponse>(request, cancellationToken);
                
                if (response == null)
                {
                    throw new TransactionException("获取AT分支事务状态失败：未收到响应");
                }

                if (response.ResultCode != ResultCode.Success)
                {
                    throw new TransactionException($"获取AT分支事务状态失败：{response.Message}");
                }

                _logger.LogDebug($"AT分支事务状态: XID={xid}, BranchId={branchId}, Status={response.Status}");
                return response.Status;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"获取AT分支事务状态失败: XID={xid}, BranchId={branchId}");
                throw new TransactionException($"获取AT分支事务状态失败: XID={xid}, BranchId={branchId}", ex);
            }
        }

        /// <summary>
        /// 同步开始AT事务
        ///
        /// 这是BeginAsync的同步包装方法，使用GetAwaiter().GetResult()阻塞等待异步操作完成。
        ///
        /// 执行流程：
        /// 1. 生成一个新的全局事务ID（使用Guid）
        /// 2. 调用BeginAsync方法异步开始事务
        /// 3. 阻塞等待异步操作完成
        /// 4. 返回生成的XID
        ///
        /// 注意事项：
        /// - 这是一个简化实现，XID由Guid生成
        /// - 生产环境中应该调用全局事务管理器生成XID
        /// - 此方法会阻塞当前线程，建议使用异步版本BeginAsync
        /// - 如果在ASP.NET环境中调用，可能导致死锁
        /// </summary>
        /// <param name="timeout">超时时间（毫秒），超时后TC会自动回滚</param>
        /// <param name="name">事务名称（可选），用于标识业务场景，当前未使用</param>
        /// <returns>全局事务ID</returns>
        /// <exception cref="TransactionException">开始事务失败时抛出</exception>
        public string Begin(int timeout, string? name = null)
        {
            // Begin方法应该启动一个新的全局事务并返回XID
            // 这里简化实现，实际应该调用全局事务管理器
            var xid = Guid.NewGuid().ToString();
            BeginAsync(xid, timeout).GetAwaiter().GetResult();
            return xid;
        }

        /// <summary>
        /// 同步提交AT事务
        ///
        /// 这是CommitAsync的同步包装方法，使用GetAwaiter().GetResult()阻塞等待异步操作完成。
        ///
        /// 执行流程：
        /// 1. 调用CommitAsync方法异步提交事务
        /// 2. 阻塞等待异步操作完成
        /// 3. 如果提交失败，异常会被抛出
        ///
        /// 注意事项：
        /// - 此方法会阻塞当前线程，建议使用异步版本CommitAsync
        /// - 如果在ASP.NET环境中调用，可能导致死锁
        /// - 提交失败会抛出TransactionException异常
        /// </summary>
        /// <param name="xid">全局事务ID</param>
        /// <exception cref="ArgumentNullException">XID为空时抛出</exception>
        /// <exception cref="TransactionException">提交失败时抛出</exception>
        public void Commit(string xid)
        {
            CommitAsync(xid).GetAwaiter().GetResult();
        }

        /// <summary>
        /// 同步回滚AT事务
        ///
        /// 这是RollbackAsync的同步包装方法，使用GetAwaiter().GetResult()阻塞等待异步操作完成。
        ///
        /// 执行流程：
        /// 1. 调用RollbackAsync方法异步回滚事务
        /// 2. 阻塞等待异步操作完成
        /// 3. 如果回滚失败，异常会被抛出
        ///
        /// 注意事项：
        /// - 此方法会阻塞当前线程，建议使用异步版本RollbackAsync
        /// - 如果在ASP.NET环境中调用，可能导致死锁
        /// - 回滚失败会抛出TransactionException异常
        /// - 回滚依赖undo_log，如果undo_log损坏或丢失，回滚会失败
        /// </summary>
        /// <param name="xid">全局事务ID</param>
        /// <exception cref="ArgumentNullException">XID为空时抛出</exception>
        /// <exception cref="TransactionException">回滚失败时抛出</exception>
        public void Rollback(string xid)
        {
            RollbackAsync(xid).GetAwaiter().GetResult();
        }

        /// <summary>
        /// 同步注册AT分支事务
        ///
        /// 这是RegisterBranchAsync的同步包装方法，使用GetAwaiter().GetResult()阻塞等待异步操作完成。
        ///
        /// 执行流程：
        /// 1. 调用RegisterBranchAsync方法异步注册分支
        /// 2. 阻塞等待异步操作完成
        /// 3. 如果注册失败，异常会被抛出
        ///
        /// 注意事项：
        /// - 此方法会阻塞当前线程，建议使用异步版本RegisterBranchAsync
        /// - 如果在ASP.NET环境中调用，可能导致死锁
        /// - 锁冲突会导致注册失败并抛出TransactionException
        /// </summary>
        /// <param name="xid">全局事务ID</param>
        /// <param name="resourceId">资源ID</param>
        /// <param name="lockKey">锁键</param>
        /// <exception cref="ArgumentNullException">XID、resourceId或lockKey为空时抛出</exception>
        /// <exception cref="TransactionException">注册失败时抛出</exception>
        public void RegisterBranch(string xid, string resourceId, string lockKey)
        {
            RegisterBranchAsync(xid, resourceId, lockKey).GetAwaiter().GetResult();
        }

        /// <summary>
        /// 同步报告分支事务状态
        ///
        /// 这是ReportStatusAsync的同步包装方法，使用GetAwaiter().GetResult()阻塞等待异步操作完成。
        ///
        /// 执行流程：
        /// 1. 调用ReportStatusAsync方法异步报告状态
        /// 2. 阻塞等待异步操作完成
        /// 3. 如果上报失败，异常会被抛出
        ///
        /// 注意事项：
        /// - 此方法会阻塞当前线程，建议使用异步版本ReportStatusAsync
        /// - 如果在ASP.NET环境中调用，可能导致死锁
        /// - 状态上报失败会抛出TransactionException异常
        /// - TC依赖状态上报来决定全局事务的最终结果
        /// </summary>
        /// <param name="xid">全局事务ID</param>
        /// <param name="branchId">分支事务ID</param>
        /// <param name="status">分支状态</param>
        /// <exception cref="ArgumentNullException">XID为空时抛出</exception>
        /// <exception cref="TransactionException">上报失败时抛出</exception>
        public void ReportBranchStatus(string xid, long branchId, BranchStatus status)
        {
            ReportStatusAsync(xid, branchId, status).GetAwaiter().GetResult();
        }
    }



    /// <summary>
    /// AT模式数据源代理接口
    ///
    /// 定义AT模式数据源代理的标准接口。
    ///
    /// 职责：
    /// 1. 创建被代理的数据库连接
    /// 2. 注册连接到代理管理器
    /// 3. 注销连接并释放资源
    ///
    /// 工作原理：
    /// - 通过代理模式拦截数据库连接的创建
    /// - 为每个连接创建AtConnectionProxy包装
    /// - AtConnectionProxy负责拦截SQL执行，自动生成undo_log
    ///
    /// 使用场景：
    /// - 应用启动时配置数据源
    /// - 业务代码获取数据库连接时自动应用AT代理
    /// - 确保所有数据库操作都在AT模式的管理下
    /// </summary>
    public interface IAtDataSourceProxy
    {
        /// <summary>
        /// 创建被代理的数据库连接
        ///
        /// 创建一个AT模式的数据库连接代理对象，该连接会自动拦截SQL执行并生成undo_log。
        ///
        /// 执行流程：
        /// 1. 创建目标数据库连接（真实的DbConnection对象）
        /// 2. 使用AtConnectionProxy包装目标连接
        /// 3. 注册连接到代理管理器
        /// 4. 返回代理连接
        ///
        /// 注意事项：
        /// - 返回的是AtConnectionProxy对象，而非真实的数据库连接
        /// - AtConnectionProxy会拦截所有SQL执行操作
        /// - 连接使用完毕后应该调用Dispose释放资源
        /// </summary>
        /// <returns>AT模式的数据库连接代理</returns>
        DbConnection CreateConnection();

        /// <summary>
        /// 注册连接到代理管理器
        ///
        /// 将数据库连接注册到代理管理器，为其创建连接上下文。
        ///
        /// 执行流程：
        /// 1. 创建AtConnectionContext上下文对象
        /// 2. 记录连接的开始时间
        /// 3. 将连接和上下文存储到字典中
        ///
        /// 注意事项：
        /// - 每个连接都有独立的上下文
        /// - 上下文包含undo_log列表
        /// - 注册后才能正常拦截SQL操作
        /// </summary>
        /// <param name="connection">待注册的数据库连接</param>
        void RegisterConnection(DbConnection connection);

        /// <summary>
        /// 注销连接并释放资源
        ///
        /// 从代理管理器中移除连接，清理连接上下文。
        ///
        /// 执行流程：
        /// 1. 从字典中移除连接和上下文
        /// 2. 清理上下文中的undo_log列表
        /// 3. 记录注销日志
        ///
        /// 调用时机：
        /// - 连接关闭时
        /// - 连接Dispose时
        /// - 事务完成后
        ///
        /// 注意事项：
        /// - 注销后连接不再被代理管理
        /// - undo_log应该在注销前已经处理完毕
        /// </summary>
        /// <param name="connection">待注销的数据库连接</param>
        void UnregisterConnection(DbConnection connection);
    }

    /// <summary>
    /// 默认AT模式数据源代理实现
    ///
    /// 职责：
    /// 1. 创建和管理AT模式的数据库连接代理
    /// 2. 为每个连接维护AtConnectionContext上下文
    /// 3. 与AT事务管理器协作完成分支事务的注册和管理
    /// 4. 管理undo_log的生成和存储
    ///
    /// 工作原理：
    /// - 使用工厂模式创建目标数据库连接
    /// - 使用代理模式包装连接为AtConnectionProxy
    /// - 使用ConcurrentDictionary存储连接上下文，支持并发访问
    /// - AtConnectionProxy拦截SQL执行，自动生成before/after image和undo_log
    ///
    /// 线程安全：
    /// - 使用ConcurrentDictionary存储连接上下文，支持并发访问
    /// - 每个连接有独立的上下文，互不干扰
    ///
    /// 使用流程：
    /// 1. 应用启动时配置DefaultAtDataSourceProxy
    /// 2. 业务代码调用CreateConnection获取连接
    /// 3. 执行SQL时AtConnectionProxy自动拦截
    /// 4. SQL执行前生成before image，执行后生成after image
    /// 5. 生成undo_log并存储到数据库
    /// 6. 连接关闭时注销连接
    ///
    /// 注意事项：
    /// - 需要提供真实的连接字符串和提供程序名称
    /// - 支持多种数据库提供程序（SQL Server、MySQL、PostgreSQL等）
    /// - 连接代理会增加一定的性能开销
    /// - undo_log需要额外的存储空间
    /// </summary>
    public class DefaultAtDataSourceProxy : IAtDataSourceProxy
    {
        private readonly IAtTransactionManager _transactionManager;
        private readonly ILogger<DefaultAtDataSourceProxy> _logger;
        private readonly ILoggerFactory _loggerFactory;
        // 连接上下文字典，key为DbConnection，value为连接上下文
        private readonly ConcurrentDictionary<DbConnection, AtConnectionContext> _connectionContexts;
        // 数据库连接字符串
        private readonly string _connectionString;
        // 数据库提供程序名称，如"System.Data.SqlClient"、"MySql.Data.MySqlClient"等
        private readonly string _providerName;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="transactionManager">AT事务管理器，用于注册和管理分支事务</param>
        /// <param name="logger">日志记录器</param>
        /// <param name="loggerFactory">日志工厂，用于创建AtConnectionProxy的日志记录器</param>
        /// <param name="connectionString">数据库连接字符串，为空时使用默认连接</param>
        /// <param name="providerName">数据库提供程序名称，默认为System.Data.SqlClient</param>
        public DefaultAtDataSourceProxy(
            IAtTransactionManager transactionManager,
            ILogger<DefaultAtDataSourceProxy> logger,
            ILoggerFactory loggerFactory,
            string connectionString = "",
            string providerName = "System.Data.SqlClient")
        {
            _transactionManager = transactionManager;
            _logger = logger;
            _loggerFactory = loggerFactory;
            _connectionContexts = new ConcurrentDictionary<DbConnection, AtConnectionContext>();
            _connectionString = connectionString;
            _providerName = providerName;
        }

        /// <summary>
        /// 创建AT模式的数据库连接代理
        ///
        /// 执行流程：
        /// 1. 根据提供程序名称获取DbProviderFactory
        /// 2. 使用工厂创建目标数据库连接
        /// 3. 设置连接字符串
        /// 4. 创建AtConnectionProxy包装目标连接
        /// 5. 注册连接到代理管理器
        /// 6. 返回代理连接
        ///
        /// 注意事项：
        /// - 如果未提供连接字符串，会记录警告日志并使用默认连接
        /// - 生产环境中应该提供真实的连接字符串
        /// - AtConnectionProxy会自动处理连接的打开、关闭和Dispose
        /// - 返回的连接应该在using语句中使用以确保资源释放
        ///
        /// 使用示例：
        /// ```csharp
        /// using (var connection = dataSourceProxy.CreateConnection())
        /// {
        ///     await connection.OpenAsync();
        ///     // 执行SQL操作，自动生成undo_log
        /// }
        /// ```
        /// </summary>
        /// <returns>AT模式的数据库连接代理</returns>
        public DbConnection CreateConnection()
        {
            // 创建目标数据库连接
            DbConnection targetConnection;
            if (!string.IsNullOrEmpty(_connectionString))
            {
                var factory = DbProviderFactories.GetFactory(_providerName);
                targetConnection = factory.CreateConnection();
                targetConnection.ConnectionString = _connectionString;
            }
            else
            {
                // 如果没有提供连接字符串，创建一个简单的测试连接
                // 实际使用时应该提供真实的连接字符串
                _logger.LogWarning("未提供连接字符串，使用默认连接");
                var factory = DbProviderFactories.GetFactory("System.Data.SqlClient");
                targetConnection = factory.CreateConnection();
            }

            var connection = new AtConnectionProxy(this, _transactionManager,
                _loggerFactory.CreateLogger<AtConnectionProxy>(), _loggerFactory, targetConnection);
            RegisterConnection(connection);
            return connection;
        }

        /// <summary>
        /// 注册连接到代理管理器
        ///
        /// 执行流程：
        /// 1. 验证连接对象不为null
        /// 2. 创建AtConnectionContext上下文对象
        /// 3. 设置连接和开始时间
        /// 4. 初始化UndoLogs列表（空列表）
        /// 5. 将连接和上下文存储到ConcurrentDictionary中
        /// 6. 记录注册成功日志
        ///
        /// 调用时机：
        /// - CreateConnection方法中自动调用
        /// - 连接创建后立即注册
        /// - 在AtConnectionProxy构造时调用
        ///
        /// 注意事项：
        /// - 连接对象为null时会抛出ArgumentNullException
        /// - 使用连接的HashCode作为日志中的ConnectionId
        /// - 上下文存储在线程安全的ConcurrentDictionary中
        /// - 重复注册同一个连接会覆盖之前的上下文
        /// </summary>
        /// <param name="connection">待注册的数据库连接</param>
        /// <exception cref="ArgumentNullException">连接对象为null时抛出</exception>
        public void RegisterConnection(DbConnection connection)
        {
            if (connection == null)
            {
                throw new ArgumentNullException(nameof(connection));
            }

            var context = new AtConnectionContext
            {
                Connection = connection,
                BeginTime = DateTime.UtcNow
            };

            _connectionContexts[connection] = context;
            _logger.LogDebug($"注册AT连接: ConnectionId={connection.GetHashCode()}");
        }

        /// <summary>
        /// 注销连接并释放资源
        ///
        /// 执行流程：
        /// 1. 验证连接对象不为null
        /// 2. 从ConcurrentDictionary中移除连接和上下文
        /// 3. 记录注销成功日志
        ///
        /// 调用时机：
        /// - 连接关闭时（AtConnectionProxy.Close）
        /// - 连接Dispose时（AtConnectionProxy.Dispose）
        /// - 事务完成后清理资源时
        ///
        /// 注意事项：
        /// - 连接对象为null时会抛出ArgumentNullException
        /// - 如果连接未注册，操作无效（不会抛出异常）
        /// - 注销后连接不再被代理管理
        /// - UndoLogs应该在注销前已经处理完毕（提交或回滚）
        /// - 使用TryRemove确保线程安全
        /// </summary>
        /// <param name="connection">待注销的数据库连接</param>
        /// <exception cref="ArgumentNullException">连接对象为null时抛出</exception>
        public void UnregisterConnection(DbConnection connection)
        {
            if (connection == null)
            {
                throw new ArgumentNullException(nameof(connection));
            }

            _connectionContexts.TryRemove(connection, out _);
            _logger.LogDebug($"取消注册AT连接: ConnectionId={connection.GetHashCode()}");
        }

        /// <summary>
        /// 获取连接的上下文
        ///
        /// 根据连接对象查询其对应的AtConnectionContext上下文。
        ///
        /// 执行流程：
        /// 1. 从ConcurrentDictionary中查询连接对应的上下文
        /// 2. 如果找到则返回上下文对象
        /// 3. 如果未找到则返回null
        ///
        /// 使用场景：
        /// - AtConnectionProxy需要获取上下文以访问UndoLogs
        /// - 分支事务需要获取上下文以记录undo_log
        /// - 事务提交或回滚时需要获取undo_log列表
        ///
        /// 注意事项：
        /// - 返回null表示连接未注册
        /// - 上下文中包含连接、开始时间和UndoLogs列表
        /// - 此方法是internal的，仅在同一程序集内可见
        /// - 线程安全：读取操作在ConcurrentDictionary中是安全的
        /// </summary>
        /// <param name="connection">数据库连接对象</param>
        /// <returns>连接上下文，如果连接未注册则返回null</returns>
        internal AtConnectionContext GetConnectionContext(DbConnection connection)
        {
            _connectionContexts.TryGetValue(connection, out var context);
            return context;
        }
    }

    /// <summary>
    /// AT连接上下文
    ///
    /// 存储单个数据库连接的AT模式相关信息。
    ///
    /// 职责：
    /// 1. 保存连接对象的引用
    /// 2. 记录连接的开始时间
    /// 3. 维护该连接产生的所有undo_log列表
    ///
    /// 生命周期：
    /// - 创建：连接注册时（RegisterConnection）
    /// - 使用：SQL执行过程中，记录undo_log
    /// - 销毁：连接注销时（UnregisterConnection）
    ///
    /// 线程安全：
    /// - 每个连接有独立的上下文实例
    /// - UndoLogs列表不是线程安全的，应该在单线程中操作
    /// - 通常一个连接只在一个线程中使用
    ///
    /// 使用场景：
    /// - AtConnectionProxy从上下文中读取UndoLogs
    /// - SQL执行时将新的undo_log添加到列表中
    /// - 事务提交时从列表中读取所有undo_log并写入数据库
    /// - 事务回滚时根据undo_log列表生成反向SQL
    /// </summary>
    internal class AtConnectionContext
    {
        /// <summary>
        /// 数据库连接对象
        ///
        /// 保存对DbConnection的引用，用于后续的SQL执行和undo_log生成。
        /// </summary>
        public DbConnection Connection { get; set; }

        /// <summary>
        /// 连接开始时间
        ///
        /// 记录连接注册时的UTC时间，用于监控和诊断连接的生命周期。
        /// </summary>
        public DateTime BeginTime { get; set; }

        /// <summary>
        /// Undo日志列表
        ///
        /// 存储该连接在当前事务中产生的所有undo_log。
        ///
        /// 用途：
        /// - SQL执行后将undo_log添加到此列表
        /// - 事务提交时将所有undo_log批量写入数据库
        /// - 事务回滚时根据undo_log生成反向SQL并执行
        ///
        /// 注意事项：
        /// - 默认初始化为空列表
        /// - 每个SQL操作可能产生一条undo_log
        /// - 列表不是线程安全的，应该在单线程中操作
        /// </summary>
        public List<AtUndoLog> UndoLogs { get; set; } = new List<AtUndoLog>();
    }

    /// <summary>
    /// AT模式回滚日志
    ///
    /// 记录SQL执行前后的数据镜像，用于事务回滚时恢复数据。
    ///
    /// 工作原理：
    /// 1. SQL执行前查询并保存修改前镜像（BeforeImage）
    /// 2. 执行业务SQL
    /// 3. SQL执行后查询并保存修改后镜像（AfterImage）
    /// 4. 将undo_log序列化后存储到数据库的undo_log表
    /// 5. 提交本地事务
    /// 6. 如果需要回滚，根据undo_log生成反向SQL恢复数据
    ///
    /// 数据结构：
    /// - Xid: 关联的全局事务ID
    /// - BranchId: 关联的分支事务ID
    /// - TableName: 受影响的表名
    /// - Sql: 执行的SQL语句（用于审计和诊断）
    /// - BeforeImage: 修改前的数据镜像（列名->值的字典）
    /// - AfterImage: 修改后的数据镜像（列名->值的字典）
    /// - CreateTime: undo_log创建时间
    ///
    /// 回滚逻辑：
    /// 1. 查询undo_log表获取对应的记录
    /// 2. 反序列化BeforeImage和AfterImage
    /// 3. 对比当前数据库的数据和AfterImage（脏写检测）
    /// 4. 根据BeforeImage生成反向SQL（UPDATE或DELETE）
    /// 5. 执行反向SQL恢复数据
    /// 6. 删除undo_log记录
    ///
    /// 使用示例：
    /// ```sql
    /// -- 原始SQL
    /// UPDATE user SET balance = 800 WHERE id = 1001;
    ///
    /// -- BeforeImage
    /// {"id": 1001, "balance": 1000}
    ///
    /// -- AfterImage
    /// {"id": 1001, "balance": 800}
    ///
    /// -- 回滚SQL（根据BeforeImage生成）
    /// UPDATE user SET balance = 1000 WHERE id = 1001;
    /// ```
    ///
    /// 注意事项：
    /// - undo_log需要序列化后存储到数据库
    /// - BeforeImage和AfterImage使用JSON格式存储
    /// - 回滚时需要检查数据是否被其他事务修改（脏写检测）
    /// - undo_log表需要有索引以提高查询效率
    /// - 提交后应该异步删除undo_log以提高性能
    /// </summary>
    public class AtUndoLog
    {
        /// <summary>
        /// 全局事务ID
        ///
        /// 标识该undo_log所属的全局事务，用于查询和管理。
        /// </summary>
        public string Xid { get; set; }

        /// <summary>
        /// 分支事务ID
        ///
        /// 标识该undo_log所属的分支事务，由TC分配。
        /// </summary>
        public long BranchId { get; set; }

        /// <summary>
        /// 表名
        ///
        /// 受SQL影响的数据库表名，用于生成反向SQL时定位表。
        /// </summary>
        public string TableName { get; set; }

        /// <summary>
        /// SQL语句
        ///
        /// 执行的原始SQL语句，用于审计、诊断和日志记录。
        /// 注意：此字段不参与回滚逻辑，仅用于记录。
        /// </summary>
        public string Sql { get; set; }

        /// <summary>
        /// 修改前镜像
        ///
        /// SQL执行前的数据快照，记录修改前的列名和值。
        ///
        /// 格式：Dictionary<列名, 值>
        /// 示例：{"id": 1001, "balance": 1000, "name": "Alice"}
        ///
        /// 用途：
        /// - 回滚时根据BeforeImage生成反向SQL
        /// - 脏写检测：对比当前数据和AfterImage
        /// - 审计：记录数据的变更历史
        /// </summary>
        public Dictionary<string, object> BeforeImage { get; set; }

        /// <summary>
        /// 修改后镜像
        ///
        /// SQL执行后的数据快照，记录修改后的列名和值。
        ///
        /// 格式：Dictionary<列名, 值>
        /// 示例：{"id": 1001, "balance": 800, "name": "Alice"}
        ///
        /// 用途：
        /// - 脏写检测：对比当前数据库的数据和AfterImage
        /// - 如果不一致，说明数据被其他事务修改，需要人工介入
        /// - 审计：记录数据的变更历史
        /// </summary>
        public Dictionary<string, object> AfterImage { get; set; }

        /// <summary>
        /// 创建时间
        ///
        /// undo_log的创建时间（UTC时间），用于：
        /// - 监控：分析事务执行时长
        /// - 清理：定期清理过期的undo_log
        /// - 审计：记录数据变更的时间点
        /// </summary>
        public DateTime CreateTime { get; set; }
    }
}